﻿using Google.Protobuf;
using MP_Game.Protos.V1;
using MP_Network.Ctrls;
using System;
using System.Net.Sockets;
using System.Threading;

namespace MP_Network
{
    public class NetClient : ServerBase
    {
        private string _host;
        private int _port;
        Timer _reconnectTimer;
        public bool _isNeedReconn = true;

        private GamePacket? _serverAuthPacket;

        private Timer _pingTimer;
        public int _pingTimeoutCounter = 0;
        public NetClient(string ip, int port, CliType clientType)
        {
            _host = ip;
            _port = port;
            _clientType = clientType;
            _connState = ConnState.Disconnected;

            InitServerAuthPacket();

            HealthCtrl healthCtrl = new();
            RegistCommand(CmdId.CmdPongRsp, healthCtrl);

            // 每隔15秒发送一次心跳包
            _pingTimer = new Timer(SendPing, null, 15_000, 15_000);
        }

        private void SendPing(object? state)
        {
            if (_connState == ConnState.Connected)
            {
                if (++_pingTimeoutCounter > 2)
                {
                    LogMsg.Warn("心跳超时，主动断开连接...");
                    Disconnect();// 将会触发重连
                    return;
                }

                var ping = new GamePacket
                {
                    CmdId = CmdId.CmdPingReq,
                    Data = new PingReq
                    {
                        ClientTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
                    }.ToByteString(),
                };
                LogMsg.Info("已发送心跳包...");
                SendData(ping);
            }
        }

        private void InitServerAuthPacket()
        {
            if (_clientType != CliType.Player)
            {
                _serverAuthPacket = new GamePacket
                {
                    CmdId = CmdId.CmdServerAuth,
                    Data = new ServerAuthReq
                    {
                        CliType = _clientType,
                        ServerId = 0
                    }.ToByteString(),
                };
            }
        }

        /// <summary>
        /// 连接服务器
        /// </summary>
        public void StartConnect()
        {
            try
            {
                if (_connState != ConnState.Disconnected) return;
                if (_socket == null)
                {
                    _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                }
                // 开始连接服务器
                _socket.BeginConnect(_host, _port, OnConnectCB, null);
            }
            catch (Exception ex)
            {
                Disconnect();
                LogMsg.Error("NetClient::StartConnect::" + ex.Message);
            }
        }

        /// <summary>
        /// 连接成功时调用
        /// </summary>
        /// <param name="ar"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void OnConnectCB(IAsyncResult ar)
        {
            try
            {
                _socket.EndConnect(ar);
                _connState = ConnState.Connected;
                // 发送验证包
                if (_serverAuthPacket != null) SendData(_serverAuthPacket);
                // 开始接收服务端发来的数据
                BeginReceive();
            }
            catch (Exception ex)
            {
                Disconnect();
                LogMsg.Error(ex.Message);
            }
        }

        /// <summary>
        /// 注册指令集
        /// </summary>
        /// <param name="cmd">指令类型</param>
        /// <param name="container">处理者</param>
        public void RegistCommand(CmdId cmd, IContainer container)
        {
            _cmdDic.Add(cmd, container);
        }

        protected override void HandleCommand(GamePacket packet)
        {
            //if (_clientType == CliType.Player)
            //{
            //    OnReceiveMsg?.Invoke(packet.CmdId, packet.Data);
            //    return;
            //}

            IContainer container = _cmdDic[packet.CmdId];
            if (container == null)
            {
                LogMsg.Error($"command {packet.CmdId} not regist!");
                return;
            }
            container.OnClientCommand(this, packet);
        }

        public override void Disconnect()
        {
            SetReconnectTimer();
            base.Disconnect();
        }

        /// <summary>
        /// 开始尝试重连服务器
        /// </summary>
        /// <exception cref="NotImplementedException"></exception>
        private void SetReconnectTimer()
        {
            if (_reconnectTimer == null)
            {
                _reconnectTimer = new Timer(Reconnect);
            }
            LogMsg.Warn("开始重连...");
            _reconnectTimer.Change(3000, 10000);
        }
        /// <summary>
        /// 重新连接服务器
        /// </summary>
        /// <param name="state"></param>
        private void Reconnect(object state)
        {
            if (_isNeedReconn)
            {
                StartConnect();
            }
        }
    }
}
